#include <iostream>

#include <yaml-cpp/yaml.h>
#include <functional>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
#include <memory>
#include "../source/config.h"
#include "../source/log.h"

void print_yaml(const YAML::Node& node, int level)
{
    if(node.IsScalar())
    {
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level*4, ' ')
            << node.Scalar() << " - Scalar -" << node.Type() << " - " << level;
    }
    else if(node.IsNull())
    {
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level*4, ' ')
            << "NULL - NULL - " << node.Type() << " - " << level;
    }
    else if(node.IsMap())
    {
        for(auto it = node.begin();
        it!=node.end();++it)
        {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level*4, ' ')
                << it->first << " - MAP - " << it->second.Type() << " - " << level;
            print_yaml(it->second, level+1);
        }
    }
    else if(node.IsSequence())
    {
        for(size_t i = 0; i < node.size(); ++i)
        {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << std::string(level*4, ' ')
                << i << " - Sequence - " << node[i].Type() << " - " << level;
                print_yaml(node[i], level+1);
        }
    }

}
void test_yaml()
{
    YAML::Node root = YAML::LoadFile("/home/linker/workspace/sylar/bin/conf/log.yml");
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << root;
    print_yaml(root, 0);
}
sylar::ConfigVar<int>::ptr g_int_val_config = sylar::Config::Lookup("system.out", (int) 8888, "system port");
sylar::ConfigVar<float>::ptr g_float_val_config = sylar::Config::Lookup("system.value", (float) 10.2, "system port");
void test_lookup()
{
    // std::cout<<g_float_val_config->getValue()<<std::endl;
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_int_val_config->getValue();
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << g_float_val_config->getValue();
}void test_vec()
{
    YAML::Node root = YAML::LoadFile("/home/linker/workspace/sylar/bin/test_cast.yml");
    YAML::Node node = root["system"]["vec"];
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "node: "<<node;
    std::stringstream ss;
    ss << node;
    sylar::ConfigVar<std::vector<int>>::ptr p_configvar(new sylar::ConfigVar<std::vector<int> >("vec", std::vector<int>{1,2}));
    std::function<std::string(const std::vector<int>&)> vec2str = [](const std::vector<int>& v)->std::string
    {
        std::stringstream ss;
        ss << "[";
        for(size_t i=0;i<v.size() ; ++i)
        {
            ss<<v[i];
            if(i!=v.size()-1) ss<<", ";
        }
        ss<<"]";
        return ss.str();
    };
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "before var: "<< vec2str(p_configvar->getValue());
    p_configvar->fromString(ss.str());
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "before var: "<< vec2str(p_configvar->getValue());
}
void test_squence()
{
    YAML::Node root = YAML::LoadFile("/home/linker/workspace/sylar/bin/test_cast.yml");
    
    std::stringstream ss;

    sylar::ConfigVar<std::vector<int>>::ptr p_configvar_vec(new sylar::ConfigVar<std::vector<int> >("vec", std::vector<int>{1,2}));
    sylar::ConfigVar<std::list<int>>::ptr p_configvar_list(new sylar::ConfigVar<std::list<int> >("list", std::list<int>{1,2}));
    sylar::ConfigVar<std::set<int>>::ptr p_configvar_set(new sylar::ConfigVar<std::set<int> >("set", std::set<int>{1,2}));
    sylar::ConfigVar<std::unordered_set<float>>::ptr p_configvar_uset(new sylar::ConfigVar<std::unordered_set<float> >("uset", std::unordered_set<float>{1,2}));
    sylar::ConfigVar<std::map<std::string, int>>::ptr p_configvar_map(new sylar::ConfigVar<std::map<std::string, int> >("set", std::map<std::string, int>{{"k1",3},{"k2",5}}));
    sylar::ConfigVar<std::unordered_map<std::string, int>>::ptr p_configvar_umap(new sylar::ConfigVar<std::unordered_map<std::string, int> >("set", std::unordered_map<std::string, int>{{"k1",-3},{"k2",15}}));
    
//     YAML::Node node = root["system"]["vec"]; 
//     SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "node: "<<node; 
//     ss.clear();
//     ss << node;
//     SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nbefore var: \n"<< p_configvar_vec->toString();
//     p_configvar_vec->fromString(ss.str()); 
//     SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nafter var: \n"<< p_configvar_vec->toString();
// /*
#define XX(p_var, type, node) \
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "node: "<<node; \
    ss.str(""); \
    ss << node; \
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nbefore var: \n"<< p_var->toString(); \
    p_var->fromString(ss.str()); \
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nafter var: \n"<< p_var->toString();
    
    // XX(p_configvar_vec, vec, root["system"]["vec"])
    // XX(p_configvar_list, list, root["system"]["list"])
    // XX(p_configvar_set, set, root["system"]["set"])
    // XX(p_configvar_uset, uset, root["system"]["unordered_set"])
    XX(p_configvar_map, map, root["system"]["map"])
    XX(p_configvar_umap, umap, root["system"]["umap"])
#undef XX
    
    //  */
}
class Person
{
public:
    Person(const std::string& name = std::string(), const int & age=0)
    :m_name(name), m_age(age){}
    std::string m_name;
    int m_age;
    std::string toString()
    {
        std::stringstream ss;
        ss<<"[Person name="<<m_name<<" age="<<m_age<<"]";
        return ss.str();
    }
    bool operator==(const Person& p) const
    {
        return this->m_name == p.m_name
                && this->m_age == p.m_age; 
    }
};
void test_loadfile()
{
    sylar::ConfigVarBase::ptr person_map= sylar::Config::Lookup("class.person_map", std::unordered_map<std::string, Person>{{"orig_person", Person()}});
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nbefore var: \n"<< person_map->toString(); 

    YAML::Node root = YAML::LoadFile("/home/linker/workspace/sylar/bin/test_cast.yml");
    sylar::Config::LoadFromYaml(root);
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nafter var: \n"<< person_map->toString();
    
}
namespace sylar
{
template<>
class LexicalCast<std::string, Person>
{
public:
    Person operator()(const std::string& v)
    {
        YAML::Node node = YAML::Load(v);
        Person p;
        p.m_name = node["name"].as<std::string>();
        p.m_age = node["age"].as<int>();
        return p;
    }
};

template<>
class LexicalCast<Person, std::string>
{
public:
    std::string operator()(const Person& p)
    {
        YAML::Node node;
        node["name"] = p.m_name;
        node["age"] = p.m_age;
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};
}

void test_listener()
{
    sylar::ConfigVarBase::ptr person_map= sylar::Config::Lookup("class.person_map", std::unordered_map<std::string, Person>{{"orig_person", Person()}});
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nbefore var: \n"<< person_map->toString(); 
    sylar::ConfigVar<std::unordered_map<std::string, Person>>::ptr p = std::dynamic_pointer_cast<sylar::ConfigVar<std::unordered_map<std::string, Person>>>(person_map);
   p->addListener([](const std::unordered_map<std::string, Person>& old_val,const std::unordered_map<std::string, Person>& new_val)->void
   {
       SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "change value" << std::endl;
        for(auto item : old_val)
        {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << item.first<<":";
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "--\t"<<"name" ":"<<item.second.m_name;
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "--\t"<<"age" ":"<<item.second.m_age;
        }

        for(auto item : new_val)
        {
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << item.first<<":";
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "--\t"<<"name" ":"<<item.second.m_name;
            SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "--\t"<<"age" ":"<<item.second.m_age;
        }

   });
    YAML::Node root = YAML::LoadFile("/home/linker/workspace/sylar/bin/test_cast.yml");
    sylar::Config::LoadFromYaml(root);
    SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "\nafter var: \n"<< person_map->toString();
    
}

void test_log()
{
    // YAML::Node root = YAML::LoadFile("/home/linker/workspace/sylar/bin/conf/log.yml");
    // SYLAR_LOG_DEBUG(SYLAR_LOG_ROOT()) << "Loaded file";
    // sylar::Config::LoadFromYaml(root);

    static sylar::Logger::ptr system_log = SYLAR_LOG_NAME("system");
    SYLAR_LOG_INFO(system_log) << "hello system" << std::endl;
    std::cout << sylar::LoggerMgr::GetInstance()->toYamlString() << std::endl;
    YAML::Node root = YAML::LoadFile("/home/linker/workspace/sylar/bin/conf/log.yml");
    sylar::Config::LoadFromYaml(root);
    std::cout << "=============" << std::endl;
    std::cout << sylar::LoggerMgr::GetInstance()->toYamlString() << std::endl;
    std::cout << "=============" << std::endl;
    std::cout << root << std::endl;
    SYLAR_LOG_INFO(system_log) << "hello system" << std::endl;

    system_log->setFormatter("%d - %m%n");
    SYLAR_LOG_INFO(system_log) << "hello system" << std::endl;


}

int main(int argc, char** argv)
{
    std::cout<<"test config..."<<std::endl;
    // SYLAR_LOG_ROOT()->setFormatter(sylar::LogFormatter::ptr(new sylar::LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%F[%p]%T[%c]%T%f:%l%T%m%n")));
    // test_yaml();
    
    // test_lookup();
    // test_vec();
    // test_squence();
    // test_loadfile();
    // std::function<void()> fun=nullptr;
    // SYLAR_LOG_DEBUG(SYLAR_LOG_ROOT()) << "fun" << std::endl;
    // test_listener();
    test_log();
    sylar::Config::Visit([](sylar::ConfigVarBase::ptr var){
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "name=" << var->getName()
                        << " discription=" << var->getDescription()
                        << " typename=" << var->getTypeName()
                        << " value=" << var->toString();
    });
    return 0;
}